using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Lightray.Hattrick.ChppLib.EntityTypes;

namespace Lightray.Hattrick.ChppLib
{
    [ComVisible(true)]
    public class PlayerScores
    {
        #region Max Rating Arrangement
        private const int MAX_RATING = 120;
        private double _forwardHundredPointsMax = ((MAX_RATING - (_experienceAdditionIndexes[20] * _formMultiplyIndexes[8])) / 247.80);
        private double _wingerHundredPointsMax = ((MAX_RATING - (_experienceAdditionIndexes[20] * _formMultiplyIndexes[8])) / 247.80);
        private double _midfieldHundredPointsMax = ((MAX_RATING - (_experienceAdditionIndexes[20] * _formMultiplyIndexes[8])) / 247.80);
        private double _wingbackHundredPointsMax = ((MAX_RATING - (_experienceAdditionIndexes[20] * _formMultiplyIndexes[8])) / 247.80);
        private double _defenderHundredPointsMax = ((MAX_RATING - (_experienceAdditionIndexes[20] * _formMultiplyIndexes[8])) / 247.80);
        private double _goalkeepingHundredPointsMax = ((MAX_RATING - (_experienceAdditionIndexes[20] * _formMultiplyIndexes[8])) / 247.80);
        private double _freekickHundredPointsMax = ((MAX_RATING - (_experienceAdditionIndexes[20] * _formMultiplyIndexes[8])) / 141.60);
        private double _penaltyHundredPointsMax = ((MAX_RATING - (_experienceAdditionIndexes[20] * _formMultiplyIndexes[8])) / 263.494);

        private void changeIndexesToHundredMax()
        {
            for (int i = 0; i < _forwardIndexes.Length; i++)
            {
                _forwardIndexes[i] *= _forwardHundredPointsMax;
                _wingerIndexes[i] *= _wingerHundredPointsMax;
                _midfieldIndexes[i] *= _midfieldHundredPointsMax;
                _wingbackIndexes[i] *= _wingbackHundredPointsMax;
                _defenderIndexes[i] *= _defenderHundredPointsMax;
                _goalkeeperIndexes[i] *= _goalkeepingHundredPointsMax;
                _freekickIndexes[i] *= _freekickHundredPointsMax;
                _penaltyIndexes[i] *= _penaltyHundredPointsMax;
            }
        }
        #endregion

        #region Indexes Definitions
        //                                                  PlyMkr  Winger  Scoring Keeping Passing Dfnding Freekik Exper   Leader
        private double[] _forwardIndexes = new double[]     { 0,      1,      7,      0,      2,      0,      0,      0,      0 };
        private double[] _wingerIndexes = new double[]      { 3,      5,      0,      0,      1,      1,      0,      0,      0 };
        private double[] _midfieldIndexes = new double[]    { 7,      0,      0,      0,      1.5,    1.5,    0,      0,      0 };
        private double[] _wingbackIndexes = new double[]    { 1,      4,      0,      0,      0,      5,      0,      0,      0 };
        private double[] _defenderIndexes = new double[]    { 2,      0,      0,      0,      0,      8,      0,      0,      0 };
        private double[] _goalkeeperIndexes = new double[]  { 0,      0,      0,      8,      0,      1,      1,      0,      0 };
        private double[] _freekickIndexes = new double[]    { 0,      0,      0,      0,      0,      0,      6,      0,      0 };
        private double[] _penaltyIndexes = new double[]     { 0,      0,      3,      0,      0,      0,      3,      4,      0 };
        private double[] _captainIndexes = new double[]     { 0,      0,      0,      0,      0,      0,      0,      3,      5 };

        private double[][] _abilityWeighMatrix;
        private static double[] _experienceAdditionIndexes = new double[]  { 0, 1.51, 2.39, 3.01, 3.5, 3.89, 4.23, 4.52, 4.77, 5, 
                                                                             5.21, 5.4, 5.57, 5.73, 5.88, 6.02, 6.15, 6.28, 6.39, 6.50, 6.60};
        private static double[] _formMultiplyIndexes = new double[]        { 0.54, 0.59, 0.66, 0.73, 0.81, 0.9, 1, 1.1, 1.18 };
        private double[] _staminaMultiplyIndexes = new double[]     { 0.50, 0.5625, 0.625, 0.6875, 0.75, 0.8125, 0.875, 0.9375, 1 };
        private void _changeIndexesAccordingToSpecialty(Specialty specialty)
        {
            switch (specialty)
            {
                case Specialty.None:
                    break;
                case Specialty.Technical:
                    _changeIndexesArray(_penaltyIndexes, 10);
                    break;
                case Specialty.Quick:
                    _changeIndexesArray(_forwardIndexes, 5);
                    _changeIndexesArray(_wingerIndexes, 5);
                    _changeIndexesArray(_defenderIndexes, 5);
                    break;
                case Specialty.Powerful:
                    break;
                case Specialty.Unpredictable:
                    _changeIndexesArray(_forwardIndexes, 5);
                    _changeIndexesArray(_wingerIndexes, 5);
                    _changeIndexesArray(_midfieldIndexes, -5);
                    _changeIndexesArray(_wingbackIndexes, -5);
                    _changeIndexesArray(_defenderIndexes, -5);
                    break;
                case Specialty.Head:
                    _changeIndexesArray(_forwardIndexes, 5);
                    _changeIndexesArray(_wingerIndexes, 5);
                    _changeIndexesArray(_midfieldIndexes, 5);
                    _changeIndexesArray(_wingbackIndexes, 5);
                    _changeIndexesArray(_defenderIndexes, 5);
                    break;
            }
        }
        private void _changeIndexesArray(double[] indexes, int changePercent)
        {
            double multiplyer = 1 + (changePercent / 100.0);
            for (int i = 0; i < indexes.Length; i++)
                indexes[i] *= multiplyer;
        }
        #endregion

        private AbilityVector _abilities;

        public double ForwardScore          { get { return calculateSkill(PlayerRole.Forward); } }
        public double WingerScore           { get { return calculateSkill(PlayerRole.Winger); } }
        public double PlaymakerScore        { get { return calculateSkill(PlayerRole.Playmaking); } }
        public double WingbackScore         { get { return calculateSkill(PlayerRole.Wingback); } }
        public double DefenderScore         { get { return calculateSkill(PlayerRole.Defender); } }
        public double GoalkeeperScore       { get { return calculateSkill(PlayerRole.Goalkeeper); } }
        public double SetPiecesScore        { get { return calculateSkill(PlayerRole.SetPieces); } }
        public double PenaltyShooterScore   { get { return calculateSkill(PlayerRole.PenaltyTaker); } }
        public double CaptainScore          { get { return calculateSkill(PlayerRole.Captain); } }

        public Player TargetPlayer { get; private set; }
        public FieldPosition AssignedPosition { get; set; }
        public Weather AffectingWeather
        {
            get { return _abilities.AffectingWeather; }
            set { _abilities.AffectingWeather = value; } 
        }
        public double AssignedPositionScore
        {
            get
            {
                switch (AssignedPosition)
                {
                    case FieldPosition.Unknown:
                        return 0;
                    case FieldPosition.Forward:
                        return ForwardScore;
                    case FieldPosition.Winger:
                        return WingerScore;
                    case FieldPosition.Playmaker:
                        return PlaymakerScore;
                    case FieldPosition.Wingback:
                        return WingbackScore;
                    case FieldPosition.Defender:
                        return DefenderScore;
                    case FieldPosition.Goalkeeper:
                        return GoalkeeperScore;
                }
                return 0;
            }
        }

        public FieldPosition GetDefaultPosition()
        {
            var scoreList = new List<Double>()
            {
                ForwardScore,
                WingerScore,
                PlaymakerScore,
                WingbackScore,
                DefenderScore,
                GoalkeeperScore,
            };

            return (FieldPosition)scoreList.IndexOf(scoreList.Max()) + 1;
        }

        public double GetScoreByPosition(FieldPosition position)
        {
            switch (position)
            {
                case FieldPosition.Unknown:
                    return 0;
                case FieldPosition.Forward:
                    return ForwardScore;
                case FieldPosition.Winger:
                    return WingerScore;
                case FieldPosition.Playmaker:
                    return PlaymakerScore;
                case FieldPosition.Wingback:
                    return WingbackScore;
                case FieldPosition.Defender:
                    return DefenderScore;
                case FieldPosition.Goalkeeper:
                    return GoalkeeperScore;
            }

            return 0;
        }

        public double GetScoreByAbility(PlayerRole ability)
        {
            switch (ability)
            {
                case PlayerRole.Forward:
                    return ForwardScore;
                case PlayerRole.Winger:
                    return WingerScore;
                case PlayerRole.Playmaking:
                    return PlaymakerScore;
                case PlayerRole.Wingback:
                    return WingbackScore;
                case PlayerRole.Defender:
                    return DefenderScore;
                case PlayerRole.Goalkeeper:
                    return GoalkeeperScore;
                case PlayerRole.SetPieces:
                    return SetPiecesScore;
                case PlayerRole.PenaltyTaker:
                    return PenaltyShooterScore;
                case PlayerRole.Captain:
                    return CaptainScore;
            }

            return 0;
        }
                
        public PlayerScores(Player target, bool teamIsCouterAttacking)
        {
            AssignedPosition = FieldPosition.Unknown;
            TargetPlayer = target;
            _abilities = new AbilityVector(target);
            _changeIndexesAccordingToSpecialty(target.Specialty);
            if (teamIsCouterAttacking)
            {
                _defenderIndexes[(int)AbilityVector.VectorIndex.Passing] = 2;
                _wingbackIndexes[(int)AbilityVector.VectorIndex.Passing] = 2;
            }
            changeIndexesToHundredMax();
            _abilityWeighMatrix = new double[][] {  _forwardIndexes, _wingerIndexes, _midfieldIndexes, _wingbackIndexes, 
                                                    _defenderIndexes, _goalkeeperIndexes, _freekickIndexes, _penaltyIndexes, _captainIndexes };
            AffectingWeather = Weather.Unknown;
        }

        public PlayerScores(Player target) :
            this(target, false)
        { }

        public PlayerScores(Player target, Weather affectingWeather, bool teamIsCouterAttacking)
            : this(target, teamIsCouterAttacking)
        {
            AffectingWeather = affectingWeather;
        }

        public override string ToString()
        {
            return TargetPlayer.Name + " Plays as " + AssignedPosition.ToString();
        }

        private double calculateSkill(PlayerRole skillToCalculate)
        {
            if ((TargetPlayer.InjuryLevel > 0) || (TargetPlayer.Cards == 3))
                return 0;

            double assumeNormal = 0;
            double[] abilityVectorArray = _abilities.ToArray();
            for (int i = 0; i < abilityVectorArray.Length; i++)
                assumeNormal += _abilityWeighMatrix[(int)skillToCalculate][i] * abilityVectorArray[i];

            if (skillToCalculate != PlayerRole.Captain)
            {
                if (TargetPlayer.InjuryLevel == 0)
                    return (((assumeNormal + _experienceAdditionIndexes[_abilities.Experience]) * _staminaMultiplyIndexes[_abilities.Stamina]) * _formMultiplyIndexes[_abilities.Form]) / 2;
                else
                    return ((assumeNormal + _experienceAdditionIndexes[_abilities.Experience]) * _staminaMultiplyIndexes[_abilities.Stamina]) * _formMultiplyIndexes[_abilities.Form];
            }
            else
                return assumeNormal;
        }
    }
}
